package com.zwjstudy.cachestudy.springcache.caffeine.config;

import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Ticker;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * ClassName: CacheConfig
 * Package: com.zwjstudy.cachestudy.springcache.caffeine.config
 * Description:
 *
 * @Author: 张文俊
 * @Create: 2025-09-19 - 19:30
 * @Version: v1.0
 */

/**
 * Caffeine配置说明：
 * initialCapacity=[integer]: 初始的缓存空间大小
 * maximumSize=[long]: 缓存的最大条数
 * maximumWeight=[long]: 缓存的最大权重
 * expireAfterAccess=[duration]: 最后一次写入或访问后经过固定时间过期
 * expireAfterWrite=[duration]: 最后一次写入后经过固定时间过期
 * refreshAfterWrite=[duration]: 创建缓存或者最近一次更新缓存后经过固定的时间间隔，刷新缓存
 * weakKeys: 打开key的弱引用
 * weakValues：打开value的弱引用
 * softValues：打开value的软引用
 * recordStats：开发统计功能
 * 注意：
 * expireAfterWrite和expireAfterAccess同事存在时，以expireAfterWrite为准。
 * maximumSize和maximumWeight不可以同时使用
 * weakValues和softValues不可以同时使用
 */
@Configuration
public class CacheConfig {


    @Bean(name = "userCacheManager",autowireCandidate = false)
    public CacheManager userCacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();

        cacheManager.setCaffeine(Caffeine.newBuilder()
                .initialCapacity(100)          // 初始容量
                .maximumSize(500)             // 最大缓存条目数
                .expireAfterWrite(Duration.of(10, ChronoUnit.MINUTES).plus(Duration.ofSeconds(new Random().nextInt(60)))) // 写入后10分钟 + 0-60秒随机过期，解决缓存雪崩
                .recordStats());              // 记录统计信息

        return cacheManager;
    }

    @Bean(name = "companyCacheManager",autowireCandidate = false)
    public CacheManager companyCacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
                .maximumSize(5)             // 最大缓存条目数
                .expireAfterWrite(10, TimeUnit.SECONDS) // 写入后10秒过期
                .recordStats());              // 记录统计信息
        return cacheManager;
    }


    /**
     * 配置 CaffeineCacheManager
     * 并指定 refreshAfterWrite 策略
     */
    @Bean(name = "autoRefreshCacheManager",autowireCandidate = false)
    public CacheManager autoRefreshCacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        // 配置Caffeine的参数
        cacheManager.setCaffeine(
                Caffeine.newBuilder()
                        .initialCapacity(100) // 初始容量
                        .maximumSize(1000) // 缓存最大条数
                        .refreshAfterWrite(10, TimeUnit.MINUTES) // 关键配置：写入10分钟后，下次访问时异步刷新
                        .expireAfterWrite(30, TimeUnit.MINUTES) // 写入30分钟后过期（作为兜底策略）
                        .recordStats() // 开启统计功能
        );
        // 非常重要：设置CacheLoader，refreshAfterWrite策略生效的前提
        cacheManager.setCacheLoader(cacheLoader());
        return cacheManager;
    }

    /**
     * 定义 CacheLoader
     * 当缓存未命中或需要刷新时，会调用此Loader来加载数据
     */
    @Bean
    public CacheLoader<Object, Object> cacheLoader() {
        return new CacheLoader<Object, Object>() {
            @Override
            public Object load(Object key) throws Exception {
                // 此方法在缓存未命中**同步**加载数据时调用
                // 例如：return yourService.loadDataFromDatabase(key);
                return null; // 实际使用时需替换为真实的数据加载逻辑
            }

            @Override
            public Object reload(Object key, Object oldValue) throws Exception {
                // 此方法在缓存需要**异步刷新**数据时调用（即refreshAfterWrite触发时）
                // 你可以在这里重新加载数据。默认实现是调用load方法，但可以重写以便更精细控制。
                // 例如：return yourService.refreshData(key, oldValue);
                return load(key); // 默认行为：直接调用load方法重新加载
            }
        };
    }

}
